<template>
  <div class="jpg-to-png-converter">
    <h2>JPG 转 PNG 转换器</h2>

    <!-- 文件上传区域 -->
    <div class="upload-area" @click="triggerFileSelect" @dragover.prevent @drop.prevent="handleDrop">
      <div v-if="!selectedFile" class="upload-placeholder">
        <i class="el-icon-upload"></i>
        <p>点击选择 JPG 图片或拖拽到此处</p>
        <p class="tip">支持 JPG、JPEG 格式</p>
      </div>

      <div v-else class="file-info">
        <img :src="previewUrl" alt="预览" class="preview-image" />
        <p>{{ selectedFile.name }}</p>
        <p>{{ formatFileSize(selectedFile.size) }}</p>
        <el-button @click.stop="removeFile" type="danger" size="small">移除</el-button>
      </div>
    </div>

    <input ref="fileInput" type="file" accept=".jpg,.jpeg" @change="handleFileSelect" style="display: none" />

    <!-- 转换选项 -->
    <div v-if="selectedFile" class="convert-options">
      <h3>转换选项</h3>
      <div class="option-item">
        <label>图片质量:</label>
        <el-slider v-model="quality" :min="0.1" :max="1" :step="0.1" show-tooltip />
        <span>{{ Math.round(quality * 100) }}%</span>
      </div>

      <div class="option-item">
        <label>保持透明背景:</label>
        <el-switch v-model="transparentBackground" />
      </div>

      <div class="option-item">
        <label>输出文件名:</label>
        <el-input v-model="outputFileName" placeholder="输出文件名" />
      </div>
    </div>

    <!-- 转换按钮 -->
    <div v-if="selectedFile" class="convert-section">
      <el-button type="primary" size="large" @click="convertImage" :loading="converting" :disabled="!selectedFile">
        {{ converting ? '转换中...' : '开始转换' }}
      </el-button>
    </div>

    <!-- 转换结果 -->
    <div v-if="convertedImage" class="result-section">
      <h3>转换结果</h3>
      <div class="result-preview">
        <img :src="convertedImage.dataUrl" alt="转换结果" class="result-image" />
        <div class="result-info">
          <p>文件名: {{ convertedImage.fileName }}</p>
          <p>文件大小: {{ formatFileSize(convertedImage.size) }}</p>
          <p>格式: PNG</p>
        </div>
      </div>

      <div class="download-section">
        <el-button type="success" @click="downloadImage">下载 PNG 图片</el-button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'

// 响应式数据
const selectedFile = ref<File | null>(null)
const previewUrl = ref<string>('')
const converting = ref<boolean>(false)
const quality = ref<number>(0.9)
const transparentBackground = ref<boolean>(true)
const outputFileName = ref<string>('')
const convertedImage = ref<{
  dataUrl: string
  fileName: string
  size: number
} | null>(null)

// DOM 引用
const fileInput = ref<HTMLInputElement>()

// 计算属性
const outputFileNameComputed = computed(() => {
  if (outputFileName.value) {
    return outputFileName.value.replace(/\.(jpg|jpeg)$/i, '') + '.png'
  }
  if (selectedFile.value) {
    return selectedFile.value.name.replace(/\.(jpg|jpeg)$/i, '.png')
  }
  return 'converted.png'
})

// 触发文件选择
const triggerFileSelect = () => {
  fileInput.value?.click()
}

// 处理文件选择
const handleFileSelect = (event: Event) => {
  const target = event.target as HTMLInputElement
  const file = target.files?.[0]
  if (file) {
    handleFile(file)
  }
}

// 处理拖拽文件
const handleDrop = (event: DragEvent) => {
  const files = event.dataTransfer?.files
  if (files && files.length > 0) {
    handleFile(files[0])
  }
}

// 处理文件
const handleFile = (file: File) => {
  // 验证文件类型
  if (!file.type.match(/^image\/(jpeg|jpg)$/)) {
    ElMessage.error('请选择 JPG 或 JPEG 格式的图片')
    return
  }

  // 验证文件大小 (限制为 10MB)
  if (file.size > 10 * 1024 * 1024) {
    ElMessage.error('文件大小不能超过 10MB')
    return
  }

  selectedFile.value = file

  // 生成预览 URL
  const reader = new FileReader()
  reader.onload = (e) => {
    previewUrl.value = e.target?.result as string
  }
  reader.readAsDataURL(file)

  // 设置默认输出文件名
  if (!outputFileName.value) {
    outputFileName.value = file.name.replace(/\.(jpg|jpeg)$/i, '')
  }

  // 清除之前的转换结果
  convertedImage.value = null
}

// 移除文件
const removeFile = () => {
  selectedFile.value = null
  previewUrl.value = ''
  outputFileName.value = ''
  convertedImage.value = null
  if (fileInput.value) {
    fileInput.value.value = ''
  }
}

// 转换图片
const convertImage = async () => {
  if (!selectedFile.value) {
    ElMessage.error('请先选择图片文件')
    return
  }

  converting.value = true

  try {
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    const img = new Image()

    await new Promise((resolve, reject) => {
      img.onload = () => {
        // 设置画布尺寸
        canvas.width = img.width
        canvas.height = img.height

        if (!ctx) {
          reject(new Error('无法获取 Canvas 上下文'))
          return
        }

        // 如果不保持透明背景，先填充白色背景
        if (!transparentBackground.value) {
          ctx.fillStyle = '#FFFFFF'
          ctx.fillRect(0, 0, canvas.width, canvas.height)
        }

        // 绘制图片
        ctx.drawImage(img, 0, 0)

        resolve(null)
      }

      img.onerror = () => {
        reject(new Error('图片加载失败'))
      }

      img.src = previewUrl.value
    })

    // 转换为 PNG
    const dataUrl = canvas.toDataURL('image/png', quality.value)

    // 计算转换后的文件大小
    const base64Data = dataUrl.split(',')[1]
    const binaryData = atob(base64Data)
    const size = binaryData.length

    convertedImage.value = {
      dataUrl,
      fileName: outputFileNameComputed.value,
      size
    }

    ElMessage.success('转换成功！')
  } catch (error) {
    console.error('转换失败:', error)
    ElMessage.error('转换失败，请重试')
  } finally {
    converting.value = false
  }
}

// 下载图片
const downloadImage = () => {
  if (!convertedImage.value) {
    ElMessage.error('没有可下载的图片')
    return
  }

  const link = document.createElement('a')
  link.href = convertedImage.value.dataUrl
  link.download = convertedImage.value.fileName
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)

  ElMessage.success('下载已开始')
}

// 格式化文件大小
const formatFileSize = (bytes: number): string => {
  if (bytes === 0) return '0 Bytes'

  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))

  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
</script>

<style scoped>
.jpg-to-png-converter {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.upload-area {
  border: 2px dashed #dcdfe6;
  border-radius: 8px;
  padding: 40px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.3s;
  margin-bottom: 20px;
}

.upload-area:hover {
  border-color: #409eff;
}

.upload-placeholder {
  color: #909399;
}

.upload-placeholder i {
  font-size: 48px;
  margin-bottom: 16px;
  display: block;
}

.upload-placeholder .tip {
  font-size: 12px;
  color: #c0c4cc;
  margin-top: 8px;
}

.file-info {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.preview-image {
  max-width: 200px;
  max-height: 200px;
  border-radius: 4px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.convert-options {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 20px;
}

.convert-options h3 {
  margin-top: 0;
  margin-bottom: 20px;
  color: #303133;
}

.option-item {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
  gap: 12px;
}

.option-item label {
  min-width: 100px;
  font-weight: 500;
}

.option-item .el-slider {
  flex: 1;
  margin: 0 12px;
}

.convert-section {
  text-align: center;
  margin-bottom: 30px;
}

.result-section {
  border-top: 1px solid #ebeef5;
  padding-top: 20px;
}

.result-section h3 {
  color: #303133;
  margin-bottom: 20px;
}

.result-preview {
  display: flex;
  gap: 20px;
  align-items: flex-start;
  margin-bottom: 20px;
  padding: 20px;
  background: #f5f7fa;
  border-radius: 8px;
}

.result-image {
  max-width: 200px;
  max-height: 200px;
  border-radius: 4px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.result-info {
  flex: 1;
}

.result-info p {
  margin: 8px 0;
  color: #606266;
}

.download-section {
  text-align: center;
}

@media (max-width: 768px) {
  .jpg-to-png-converter {
    padding: 10px;
  }

  .upload-area {
    padding: 20px;
  }

  .result-preview {
    flex-direction: column;
    text-align: center;
  }

  .option-item {
    flex-direction: column;
    align-items: stretch;
  }

  .option-item label {
    min-width: auto;
    margin-bottom: 8px;
  }
}
</style>
